package com.imooc.filter;

import com.google.gson.Gson;
import com.imooc.base.BaseInfoProperties;
import com.imooc.grace.result.GraceJSONResult;
import com.imooc.grace.result.ResponseStatusEnum;
import com.imooc.utils.JWTUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.http.HttpStatus;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.util.AntPathMatcher;
import org.springframework.util.MimeTypeUtils;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.List;

/**
 * @author 啊昌
 * @date 2025/9/12 23:05
 * SecurityFilterJWT 放行白名单，然后拦截token进行校验
 */
@Component
@Slf4j
public class SecurityFilterJWT extends BaseInfoProperties implements GlobalFilter, Ordered {

	public static final String HEADER_USER_TOKEN = "headerUserToken";

	@Autowired
	private ExcludeUrlProperties excludeUrlProperties;
	@Autowired
	private JWTUtils jwtUtils;


	// 路径匹配的规则器
	private AntPathMatcher antPathMatcher = new AntPathMatcher();

	/**
	 * 网关过滤器方法，用于对请求进行白名单校验和token验证
	 * @param exchange 服务器Web交换对象，包含请求和响应信息
	 * @param chain 网关过滤器链，用于继续执行后续过滤器
	 * @return Mono<Void> 异步无返回值对象，表示过滤器处理完成
	 */
	@Override
	public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
		// 获取到白名单 和 请求的url 进行对比
		ServerHttpRequest request = exchange.getRequest();
		List<String> urls = excludeUrlProperties.getUrls();  //白名单列表
		String path = request.getURI().getPath(); //获取到请求的 url
		log.info("当前请求的url：{}", path);
		// 白名单路径校验
		if (urls != null && !urls.isEmpty()) {
			//如果匹配，则放行
			for (String url : urls) {
				if (antPathMatcher.match(url, path)) {
					return chain.filter(exchange);
				}
			}
		}

		//静态资源放行
		String fileStart = excludeUrlProperties.getFileStart();
		if (StringUtils.isNotBlank(fileStart)) {
			boolean matchStartFiles = antPathMatcher.matchStart(fileStart, path);
			if (matchStartFiles) {
				// 如果匹配到，则直接放行
				return chain.filter(exchange);
			}
		}


		log.info("当前请求的url：{} 被拦截了", path);
		// 如果不匹配，则进行token校验
		String UserToken = request.getHeaders().getFirst(HEADER_USER_TOKEN);
		if(StringUtils.isNoneBlank(UserToken)){
			// 表示携带了token
			// 对token进行分割 token -> app@xxxx
			String[] tokenArr = UserToken.split(JWTUtils.at);
			if(tokenArr.length <  2 ){
				return renderErrorMsg(exchange, ResponseStatusEnum.UN_LOGIN);
			}

			// 获得jwt的令牌与前缀
			String prefix = tokenArr[0];
			String jwt = tokenArr[1];

			// 更加不同的用户类型设置不同的请求头
			if (prefix.equalsIgnoreCase(TOKEN_USER_PREFIX)) {
				return dealJWT(jwt, exchange, chain, APP_USER_JSON);
			} else if (prefix.equalsIgnoreCase(TOKEN_SAAS_PREFIX)) {
				return dealJWT(jwt, exchange, chain, SAAS_USER_JSON);
			} else if (prefix.equalsIgnoreCase(TOKEN_ADMIN_PREFIX)) {
				return dealJWT(jwt, exchange, chain, ADMIN_USER_JSON);
			}
		}

		// token校验失败，返回未登录错误信息
		return renderErrorMsg(exchange, ResponseStatusEnum.UN_LOGIN); // 返回错误信息 -> 自定义数据返回
	}


	/**
	 * 重新包装并且返回错误信息
	 *
	 * @param exchange
	 * @param statusEnum
	 * @return
	 */
	public Mono<Void> renderErrorMsg(ServerWebExchange exchange,
	                                 ResponseStatusEnum statusEnum) {
		// 1. 获得response
		ServerHttpResponse response = exchange.getResponse();

		// 2. 构建jsonResult
		GraceJSONResult jsonResult = GraceJSONResult.exception(statusEnum);

		// 3. 修改response的code为500
		response.setStatusCode(HttpStatus.INTERNAL_SERVER_ERROR);

		// 4. 设定header类型
		if (!response.getHeaders().containsKey("Content-Type"))
			response.getHeaders().add("Content-Type", MimeTypeUtils.APPLICATION_JSON_VALUE);

		// 5. 转换json并且向response中写入数据
		String resultJson = new Gson().toJson(jsonResult);
		DataBuffer dataBuffer = response
				.bufferFactory()
				.wrap(resultJson.getBytes(StandardCharsets.UTF_8));
		return response.writeWith(Mono.just(dataBuffer));
	}



	/**
	 * 处理JWT令牌的网关过滤器方法
	 * @param jwt JWT令牌字符串
	 * @param exchange 服务器Web交换对象，包含请求和响应信息
	 * @param chain 网关过滤器链，用于继续执行后续过滤器
	 * @param key 用于JWT验证的密钥
	 * @return Mono<Void> 异步响应式编程的空返回值
	 */
		public Mono<Void> dealJWT(String jwt, ServerWebExchange exchange, GatewayFilterChain chain, String key) {
			String userJson = jwtUtils.checkJWT(jwt); // 校验token 返回用户的信息
			if(userJson == null){
				return renderErrorMsg(exchange, ResponseStatusEnum.UN_LOGIN);
			}
			// 重新设置一下新的request 携带新的请求头数据
//			ServerWebExchange newExchange = setNewHeader(exchange, HEADER_USER_TOKEN, jwt);
			ServerWebExchange newExchange = setNewHeader(exchange, key, userJson);
			return chain.filter(newExchange);
		}

	/**
	 * 设置新的请求头信息
	 *
	 * @param exchange ServerWebExchange对象，用于获取和修改HTTP请求
	 * @param headerKey 请求头的键
	 * @param headerValue 请求头的值
	 * @return 返回修改后的ServerWebExchange对象，包含新的请求头信息
	 */
	public ServerWebExchange setNewHeader(ServerWebExchange exchange,
	                                      String headerKey,
	                                      String headerValue) {
		try {

			log.info("headerKey = {}" + headerKey);
			log.info("headerValue = {}" + headerValue);
			// 对请求头值进行URL编码，防止特殊字符导致的问题
			headerValue = URLEncoder.encode(headerValue, StandardCharsets.UTF_8.toString());
		} catch (UnsupportedEncodingException e) {
			throw new RuntimeException(e);
		}

		// 请求到网关的时候 重新更新一下请求头，让新的request携带新的请求头
		ServerHttpRequest NewRequest = exchange.getRequest()
				.mutate()
				.header(headerKey, headerValue)
				.build();
		return  exchange.mutate().request(NewRequest).build();
	}


		@Override
	public int getOrder() {
		return 0;
	}
}
